﻿using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;


namespace DailyFx.UI
{

	public class UITaskManager : TaskScheduler
	{
		/// <summary>
		///		tasks waiting to run .
		/// </summary>
		private readonly ConcurrentQueue<Taskimpl> _waitingQueue;

		/// <summary>
		///		tasks about to run .
		/// </summary>
		private readonly RingBuffer<Taskimpl> _workQueue;

		/// <summary>
		///		periodic cycle for task execution .
		/// </summary>
		private readonly int _period;

		/// <summary>
		///		threshold for suspension .
		/// </summary>
        private readonly double _threshold;

	
		/// <summary>
		///		the factory to create new task instance(s) .
		/// </summary>
		private readonly TaskFactory _factory ;


		/// <summary>
		/// 
		/// </summary>
        public UITaskManager ()
        {
			this._waitingQueue = new ConcurrentQueue<Taskimpl>();
			this._workQueue = new RingBuffer<Taskimpl>( 2048 );

			this._period = 50; // (ms)
			this._threshold = 3000; //(ms)

			//
			this._factory = new TaskFactory( TaskCreationOptions.LongRunning, TaskContinuationOptions.None );

			//
            var executor = new System.Timers.Timer( this._period );
            executor.Elapsed += this._WaitTaskimpl ;
            executor.Start();

            for ( int i = 0; i < Environment.ProcessorCount; i++ )
            {
                _factory.StartNew(() =>
                {
                    while (true)
                    {
                        var task = this._workQueue.Pop() ;
#if DEBUG
						// log it .
#endif    
                        if ( TryExecuteTask(task) )
                        {
                            if ( DateTime.UtcNow > task.Deadline )
                            {
                                //throw new ApplicationException("Real-time task deadline exceeded.");
                            }
#if DEBUG
							// log it .
#endif
                        }
                    }
                });
            }    
        }
		
		
		public void RunTask ( Delegate action, object[] args, TaskKind taskKind, double budget )
		{
			var task = new Taskimpl( () => action.DynamicInvoke( args), taskKind, budget );
			task.Start( this );
		}



		protected override void QueueTask ( Task task )
		{
#if DEBUG
			// Common.Check.MustValid ( task is Taskimpl );
#endif
			Taskimpl t = task as Taskimpl;
            // jump the queue
            if ( t.TaskKind == TaskKind.Sporadic )
            {
                TryExecuteTask ( t) ;
                return;
            }
            
            _waitingQueue.Enqueue ( task as Taskimpl ); 
		}


		/**
		protected override bool TryExecuteTask(Task task)
		{
			return base.TryExecuteTask( task );
		}
		*/

		protected override bool TryExecuteTaskInline ( Task task, bool taskWasPreviouslyQueued )
        {
            return TryExecuteTask ( task );
        }

		private void _WaitTaskimpl /*Until Ready to Run*/(object sender, System.Timers.ElapsedEventArgs e)
        {
            Taskimpl task ;
            while ( this._waitingQueue.TryDequeue(out task) )
            {
                // check budget and invoke a context switch
                if ( task != null )
                {
                    var next = this._workQueue.Peek();

                    if ( (next != null) && ( next.Status == TaskStatus.WaitingToRun )
						&& task.Deadline < next.Deadline )
                    {
						var t2 = _workQueue.Pop ();
						this._workQueue.Push (task);
						this._workQueue.Push (t2);
                    }
                    else
                    {
						this._workQueue.Push ( task);
                    }
                    
                }  
            }
        }


        protected override IEnumerable<Task> GetScheduledTasks ()
        {
            return _waitingQueue.ToArray();
        }
	}


	
}
